using System;
using System.Collections;
using System.Security.Principal;

namespace Sedna.Core.Security
{
	/// <summary>
	/// takes a IDictionary of role names an maps values to keys.
	/// E.g. if an internal names are: users, managers and external names are: MyDomain\Sedna_Users, MyDomain\Sedna_Managers
	/// then the keys of that dictionary will be 'users' and 'managers', whereas their corresponding
	/// values will be 'MyDomain\Sedna_Users' and 'MyDomain\Sedna_Managers'. In other words, 
	/// those principals whose call <c>principal.IsInRole('MyDomain\Sedna_Users')==true</c> will
	/// get 'users' role assigned, and those whose call <c>principal.IsInRole('MyDomain\Sedna_Managers')==true</c> will
	/// get 'managers' role assigned.
	/// 
	/// It is consequently possible to map one external role (or NTLM group) to several different
	/// internal System roles, whereas the opposit is not possible since the internal role is the IDitionary key 
	/// thus must be unique.
	/// </summary>
	public class OneToOneRoleMapper : IRoleMapper
	{

		private IDictionary internal2external = new Hashtable();

		public IDictionary Internal2external
		{
			set { internal2external = value; }
		}


		/// <summary>
		/// checks if a particular principal has every role specified in the mapping dictionary
		/// those roles that a principal has will be added into the result list.
		/// </summary>
		/// <param name="principal">a currently logged in principal whose authorization is in question</param>
		/// <returns>a list of internal role names that corresponds those external roles a principal is in</returns>
		public string[] MapPrincipalToRoles(IPrincipal principal)
		{
			ArrayList principalRoles = new ArrayList();
			for(IDictionaryEnumerator en = internal2external.GetEnumerator(); en.MoveNext();)
			{
				if(principal.IsInRole((string)en.Entry.Value))
				{
					principalRoles.Add(en.Entry.Key);
				}
			}

			return (string[])principalRoles.ToArray(typeof(string));
		}

		/// <summary>
		/// the same as above, but instead of calling the IsInRole for a principal, 
		/// just checks if a particular role is contained within the passed list.
		/// </summary>
		/// <param name="externalRoles">a list of external role that an implied principal is supposed to have</param>
		/// <returns>returns a list of internal role names that corresponds to the list of roles passed</returns>
		public string[] MapPrincipalToRoles(IList externalRoles)
		{
			ArrayList principalRoles = new ArrayList();
			for(IDictionaryEnumerator en = internal2external.GetEnumerator(); en.MoveNext();)
			{
				if(externalRoles.Contains((string)en.Entry.Value))
				{
					principalRoles.Add(en.Entry.Key);
				}
			}

			return (string[])principalRoles.ToArray(typeof(string));
		}
	}
}
